package com.zoweunion.mechanic.plugins.shiro;

import org.apache.commons.lang.ArrayUtils;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataRangMyBatisInterceptor implements Interceptor {

    private static final Logger logger = LoggerFactory.getLogger(DataRangMyBatisInterceptor.class);

    private JdbcTemplate jdbcTemplate;

    public DataRangMyBatisInterceptor(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        // 通过MetaObject优雅访问对象的属性，这里是访问statementHandler的属性;：MetaObject是Mybatis提供的一个用于方便、
        // 优雅访问对象属性的对象，通过它可以简化代码、不需要try/catch各种reflect异常，同时它支持对JavaBean、Collection、Map三种类型对象的操作。
        MetaObject metaObject = MetaObject
                .forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
                        new DefaultReflectorFactory());
        // 先拦截到RoutingStatementHandler，里面有个StatementHandler类型的delegate变量，其实现类是BaseStatementHandler，然后就到BaseStatementHandler的成员变量mappedStatement
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        // id为执行的mapper方法的全路径名，如 com.zoweunion.mechanic.dao.base.OwnerDao.countOwner
        String id = mappedStatement.getId();
        // logger.info("do op datarange sql" + id);

        // 只有在需要处理的语句数组中的SQL才需要处理数据范围的逻辑
        if (ArrayUtils.contains(strArr, id)) {
            ParameterHandler parameter = statementHandler.getParameterHandler();
            Map<String, Object> parameterMap = new HashMap<>();
            if (id.equals("com.zoweunion.mechanic.dao.TSSsVehicleModelDao.mapListNew")
                    || id.equals("com.zoweunion.mechanic.dao.TSSsVehicleModelDao.mapListNew_COUNT")
                    || id.equals("com.zoweunion.mechanic.dao.TSSsVehicleModelDao.selectCarNew")) {
                Map<String, Object> parameterMapObj = (Map<String, Object>) parameter.getParameterObject();
                parameterMap = (Map<String, Object>) parameterMapObj.get("map");
            } else {
                parameterMap = (Map<String, Object>) parameter.getParameterObject();
            }
            //parameterMap.get("isServer") != null && !parameterMap.get("isServer").equals(1) 主机厂用户不用过滤(销售等角色）
            if (parameterMap.get("current_logined_user_r_id") != null && parameterMap.get("isServer") != null && !parameterMap.get("isServer").equals(1)) {
                String currentLogined_user_r_id = parameterMap.get("current_logined_user_r_id").toString();
                // 需要处理角色ID(r_id): 10 销售的r_id;
                if (currentLogined_user_r_id.equals("10")) {
                    logger.info("................start do op datarange sql................");

                    String sId = parameterMap.get("s_id").toString();
                    String currentLoginedUserId = parameterMap.get("current_logined_user_id").toString();

                    // 查询当前登陆者(销售)可以查看的车主的主键ID的列表(当前登陆者(销售)设置的省市区包含车主的省市区的车主)
                    List userIdList = jdbcTemplate.queryForList("select concat('''', id , '''') as userId from " +
                            "(select ou.id, ou.user_account, ou.user_name, ou.area from t_s_ss_user ou " +
                            "where ou.r_id = 5 and ou.s_id = '" + sId + "' and ou.delete_flag = 0 " +
                            "and ou.area in (select area_number from t_s_ss_assign_region where user_id = '" + currentLoginedUserId + "') group by id) re ");


                    // 由于mysql的group_concat存在长度限制(默认为1024)，超出限制的字符会被截断，故循环处理IDS自行拼接
                    Map<String, Object> idsMap = new HashMap<>();
                    StringBuilder ids = new StringBuilder("");
                    userIdList.forEach(item -> {
                        Map<String, Object> currentMap = (Map<String, Object>)item;
                        ids.append(currentMap.get("userId") + ",");
                    });
                    ids.setLength(ids.length() - 1);
                    idsMap.put("ids", ids.toString());

                    //获取到原始sql语句
                    BoundSql boundSql = statementHandler.getBoundSql();
                    String sql = boundSql.getSql();
                    //sql = sql.toLowerCase();

                    String mSql = sql;
                    // 如果当前拦截到是查询分页(包括 PageHelper)语句，则需要在分页SQL关键字前面加and过滤条件
                    if (id.equals("com.zoweunion.mechanic.dao.TSSsVehicleModelDao.mapListNew") || id.equals("com.zoweunion.mechanic.dao.TSSsVehicleModelDao.mapListNew_COUNT") || id.equals("com.zoweunion.mechanic.dao.TSSsVehicleModelDao.selectCarNew")) {
                        if (id.equals("com.zoweunion.mechanic.dao.TSSsVehicleModelDao.selectCarNew")) {
                            int limitIndex = sql.lastIndexOf("where");
                            String sqlFirstPart = sql.substring(0, limitIndex); // where前面的SQL片段
                            String sqlLastPart = sql.substring(limitIndex, sql.length());  // whereSQL片段
                            sqlFirstPart += " left join t_s_br_owner_car b on bc.id = b.c_id " +
                                    " left join t_s_ss_user c on c.id = b.o_id ";
                            String sqlOne = sqlFirstPart + sqlLastPart;

                            int limitIndexOne = sqlOne.indexOf("and");
                            String sqlFirstPartOne = sqlOne.substring(0, limitIndexOne); // and前面的SQL片段
                            String sqlLastPartOne = sqlOne.substring(limitIndexOne, sqlOne.length());  // andSQL片段
                            sqlFirstPartOne += " and c.id in" + "(" + idsMap.get("ids") + ") ";
                            mSql = sqlFirstPartOne + sqlLastPartOne;
                        } else if (id.equals("com.zoweunion.mechanic.dao.TSSsVehicleModelDao.mapListNew") || id.equals("com.zoweunion.mechanic.dao.TSSsVehicleModelDao.mapListNew_COUNT")) {
                            if (sql.contains("group")) {
                                int limitIndex = sql.lastIndexOf("group");
                                String sqlFirstPart = sql.substring(0, limitIndex); // group前面的SQL片段
                                String sqlLastPart = sql.substring(limitIndex, sql.length());  // groupSQL片段
                                sqlFirstPart += " and c.id in" + "(" + idsMap.get("ids") + ") ";
                                mSql = sqlFirstPart + sqlLastPart;
//                        } else { // 非分页SQL语句，如分页countSQL语句
//                            mSql += " and c.id in" + "(" + idsMap.get("ids") + ") ";
                            }
                        }
                    } else if (id.equals("com.zoweunion.mechanic.dao.app.ToolCarDataMapper.listForCarNew")) {
                        int whereIndex = sql.indexOf("where");
                        String sqlFirstPart = sql.substring(0, whereIndex); // where前面的SQL片段
                        String sqlLastPart = sql.substring(whereIndex, sql.length());  // whereSQL片段
                        sqlFirstPart += " left join t_s_br_owner_car b on bc.id = b.c_id " +
                                " left join t_s_ss_user c on c.id = b.o_id ";
                        String sqlOne = sqlFirstPart + sqlLastPart;

                        int orderIndex = sqlOne.lastIndexOf("order");
                        String sqlFirstPartOne = sqlOne.substring(0, orderIndex); // order前面的SQL片段
                        String sqlLastPartOne = sqlOne.substring(orderIndex, sqlOne.length());  // orderSQL片段
                        sqlFirstPartOne += " and c.id in" + "(" + idsMap.get("ids") + ") ";
                        mSql = sqlFirstPartOne + sqlLastPartOne;
                    } else if (id.equals("com.zoweunion.mechanic.dao.app.ToolCarDataMapper.listCarPage") || id.equals("com.zoweunion.mechanic.dao.app.ToolCarDataMapper.listCarPage_COUNT")) {
                        int whereIndex = sql.indexOf("where");
                        String sqlFirstPart = sql.substring(0, whereIndex); // where前面的SQL片段
                        String sqlLastPart = sql.substring(whereIndex, sql.length());  // whereSQL片段
                        sqlFirstPart += " left join t_s_ss_user c on c.id = oc.o_id ";
                        String sqlOne = sqlFirstPart + sqlLastPart;

                        int orderIndex = sqlOne.lastIndexOf("order");
                        String sqlFirstPartOne = sqlOne.substring(0, orderIndex); // order前面的SQL片段
                        String sqlLastPartOne = sqlOne.substring(orderIndex);  // orderSQL片段
                        sqlFirstPartOne += " and c.id in" + "(" + idsMap.get("ids") + ") ";
                        mSql = sqlFirstPartOne + sqlLastPartOne;
                    } else if (id.equals("com.zoweunion.mechanic.dao.TSSsVehicleModelDao.getCarOwnerListForCarWorkPerRang") || id.equals("com.zoweunion.mechanic.dao.TSSsVehicleModelDao.getCarOwnerListForCarOwnerRang")) {
                        int orderIndex = sql.lastIndexOf("order");
                        String sqlFirstPart = sql.substring(0, orderIndex); // order前面的SQL片段
                        String sqlLastPart = sql.substring(orderIndex, sql.length());  // orderSQL片段
                        sqlFirstPart += " and o_id in" + "(" + idsMap.get("ids") + ") ";
                        mSql = sqlFirstPart + sqlLastPart;
                    } else if (id.equals("com.zoweunion.mechanic.dao.TSSsVehicleModelDao.getCarOwnerListForCarWorkPerRang_COUNT") || id.equals("com.zoweunion.mechanic.dao.TSSsVehicleModelDao.getCarOwnerListForCarOwnerRang_COUNT")) {
                        mSql += " and o_id in" + "(" + idsMap.get("ids") + ") ";
                    } else {
                        if (sql.contains("LIMIT")) {
                            int limitIndex = sql.lastIndexOf("LIMIT");
                            String sqlFirstPart = sql.substring(0, limitIndex); // limit前面的SQL片段
                            String sqlLastPart = sql.substring(limitIndex, sql.length());  // limitSQL片段
                            sqlFirstPart += " and u.id in" + "(" + idsMap.get("ids") + ") ";
                            mSql = sqlFirstPart + sqlLastPart;
                        } else { // 非分页SQL语句，如分页countSQL语句
                            mSql += " and u.id in" + "(" + idsMap.get("ids") + ") ";
                        }
                    }

                    // 通过反射修改SQL语句
                    Field field = boundSql.getClass().getDeclaredField("sql");
                    field.setAccessible(true);
                    field.set(boundSql, mSql);
                }
            }
        }

        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

    // 需要处理的MyBatis语句的ID数组集合，如 com.zoweunion.mechanic.dao.base.OwnerDao.countOwner
    private String[] strArr = {
            "com.zoweunion.mechanic.dao.base.OwnerDao.pageQuery",
            "com.zoweunion.mechanic.dao.base.OwnerDao.countOwner",
            "com.zoweunion.mechanic.dao.TSSsVehicleModelDao.mapListNew",
            "com.zoweunion.mechanic.dao.TSSsVehicleModelDao.mapListNew_COUNT",
            "com.zoweunion.mechanic.dao.TSSsVehicleModelDao.selectCarNew",
            "com.zoweunion.mechanic.dao.app.ToolCarDataMapper.listForCarNew",
            "com.zoweunion.mechanic.dao.app.ToolCarDataMapper.listCarPage",
            "com.zoweunion.mechanic.dao.app.ToolCarDataMapper.listCarPage_COUNT",
            "com.zoweunion.mechanic.dao.TSSsVehicleModelDao.getCarOwnerListForCarWorkPerRang",
            "com.zoweunion.mechanic.dao.TSSsVehicleModelDao.getCarOwnerListForCarWorkPerRang_COUNT",
            "com.zoweunion.mechanic.dao.TSSsVehicleModelDao.getCarOwnerListForCarOwnerRang",
            "com.zoweunion.mechanic.dao.TSSsVehicleModelDao.getCarOwnerListForCarOwnerRang_COUNT"

    };

    // 复制原始的MappedStatement
    private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
        MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource,
                ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null) {
            for (String keyProperty : ms.getKeyProperties()) {
                builder.keyProperty(keyProperty);
            }
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.cache(ms.getCache());
        builder.useCache(ms.isUseCache());
        return builder.build();
    }

    public static class BoundSqlSqlSource implements SqlSource {
        BoundSql boundSql;

        public BoundSqlSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }

        public BoundSql getBoundSql(Object parameterObject) {
            return boundSql;
        }
    }
}